﻿using System;
using Helpers;
using System.Collections.Generic;
using System.Linq;
using MarriageFertility.DataType;
using MarriageFertility.Helpers;
using MarriageFertility.Mcm;
using MarriageFertility.Texts;
using TaleWorlds.CampaignSystem;
using TaleWorlds.CampaignSystem.Actions;
using TaleWorlds.CampaignSystem.CampaignBehaviors;
using TaleWorlds.CampaignSystem.ComponentInterfaces;
using TaleWorlds.CampaignSystem.LogEntries;
using TaleWorlds.CampaignSystem.Party;
using TaleWorlds.CampaignSystem.Settlements;
using TaleWorlds.Core;
using TaleWorlds.Library;
using TaleWorlds.Localization;

namespace MarriageFertility.Behaviors;

public class CustomerPregnancyCampaignBehavior : PregnancyCampaignBehavior
{
    
    private List<CustomerPregnancy> _heroPregnancies = new();

    private List<CustomerPregnancy> HeroPregnancies
    {
        get
        {
            if (_heroPregnancies == null) _heroPregnancies = new List<CustomerPregnancy>();
            return _heroPregnancies;
        }
    }
    
    public override void RegisterEvents()
    {
        CampaignEvents.HeroKilledEvent.AddNonSerializedListener(this, OnHeroKilled);
        CampaignEvents.DailyTickHeroEvent.AddNonSerializedListener(this, DailyTickHero);
        CampaignEvents.OnChildConceivedEvent.AddNonSerializedListener(this, ChildConceived);
        CampaignEvents.DailyTickPartyEvent.AddNonSerializedListener(this, PrisonerPregnancy);
        CampaignEvents.DailyTickSettlementEvent.AddNonSerializedListener(this, SettlementPregnancy);
        CampaignEvents.DailyTickHeroEvent.AddNonSerializedListener(this, ChildrenIncreaseAge);
    }
    
    public void RemoveHeroPregnancy(Hero hero)
    {
        if (hero == null) return;
        _heroPregnancies.RemoveAll(p => p.Mother == hero);
        if (hero.IsPregnant)
        {
            hero.IsPregnant = false;
        }
    }
    
    private void DailyTickHero(Hero hero)
    {
        if (!hero.IsFemale  || !hero.IsAlive || hero.Age <= Campaign.Current.Models.AgeModel.HeroComesOfAge || hero.Clan != null && hero.Clan.IsRebelClan) return;
        
        if (hero.Age >= MCMSettings.Instance.MinPregnancyAge && hero.Age <= MCMSettings.Instance.MaxPregnancyAge &&
            hero.Spouse != null && hero.Spouse.IsAlive && !hero.IsPregnant) RefreshSpouseVisit(hero);
        
        
        if (!hero.IsPregnant) return;
        CheckOffspringsToDeliver(hero);
    }
    
    private void CheckOffspringsToDeliver(Hero hero)
    {
        CustomerPregnancy pregnancy = HeroPregnancies.Find(x => x.Mother == hero);
        if (pregnancy == null)
            hero.IsPregnant = false;
        else
            CheckOffspringsToDeliver(pregnancy);
    }

    private void RefreshSpouseVisit(Hero hero)
    {
        if (!CheckAreNearby(hero, hero.Spouse) || MBRandom.RandomFloat >
            Campaign.Current.Models.PregnancyModel.GetDailyChanceOfPregnancyForHero(hero))
            return;
        MakePregnantAction.Apply(hero);
    }

    private bool CheckAreNearby(Hero hero, Hero spouse)
    {
        Settlement heroSettlement1;
        MobileParty heroParty1;
        GetLocation(hero, out heroSettlement1, out heroParty1);
        Settlement heroSettlement2;
        MobileParty heroParty2;
        GetLocation(spouse, out heroSettlement2, out heroParty2);
        return heroSettlement1 != null && heroSettlement1 == heroSettlement2 ||
               heroParty1 != null && heroParty1 == heroParty2 || hero.Clan != Hero.MainHero.Clan &&
               MBRandom.RandomFloat < 0.2f;
    }

    private void GetLocation(Hero hero, out Settlement heroSettlement, out MobileParty heroParty)
    {
        heroSettlement = hero.CurrentSettlement;
        heroParty = hero.PartyBelongedTo;
        if (heroParty?.AttachedTo != null)
            heroParty = heroParty.AttachedTo;
        if (heroSettlement != null)
            return;
        heroSettlement = heroParty?.CurrentSettlement;
    }

    private void CheckOffspringsToDeliver(CustomerPregnancy pregnancy)
    {
        PregnancyModel pregnancyModel = Campaign.Current.Models.PregnancyModel;
        Hero mother = pregnancy.Mother;

        if (pregnancy.DueDate.IsFuture || !mother.IsAlive) 
        {
            return;
        }

        int numOffspring = MBRandom.RandomFloat <= pregnancyModel.DeliveringTwinsProbability ? 2 : 1;
        List<Hero> aliveChildren = new List<Hero>();
        int stillbornCount = 0;

        for (int i = 0; i < numOffspring; i++)
        {
            if (MBRandom.RandomFloat > pregnancyModel.StillbirthProbability)
            {
                bool isOffspringFemale = MBRandom.RandomFloat <= pregnancyModel.DeliveringFemaleOffspringProbability;
                try
                {
                    Hero offspring = HeroCreator.DeliverOffSpring(mother, pregnancy.Father, isOffspringFemale);
                    aliveChildren.Add(offspring);
                }
                catch (Exception)
                {
                    continue;
                }
            }
            else
            {
                DisplayStillbornMessage(mother);
                stillbornCount++;
            }
        }

        CampaignEventDispatcher.Instance.OnGivenBirth(mother, aliveChildren, stillbornCount);
        mother.IsPregnant = false;
        HeroPregnancies.Remove(pregnancy);

        if (mother == Hero.MainHero || MBRandom.RandomFloat > pregnancyModel.MaternalMortalityProbabilityInLabor)
        {
            return;
        }

        KillCharacterAction.ApplyInLabor(mother);
    }

    private void DisplayStillbornMessage(Hero mother)
    {
        TextObject parent = new TextObject("{=pw4cUPEn}{MOTHER.LINK} has delivered stillborn.");
        StringHelpers.SetCharacterProperties("MOTHER", mother.CharacterObject, parent);
        InformationManager.DisplayMessage(new InformationMessage(parent.ToString()));
    }


    private void ChildrenIncreaseAge(Hero hero)
    {
        if (!MCMSettings.Instance.ChildrenBecomeHuman || hero.Age >= 18 || !hero.IsAlive) return;
        hero.SetBirthDay(hero.BirthDay - CampaignTime.Days(MCMSettings.Instance.ChildrenBecomeHumanIncreaseAge));
    }

    private void PrisonerPregnancy(MobileParty mobileParty)
    {
        if (!MCMSettings.Instance.PrisonPregnancyEnable || mobileParty.LeaderHero == null ||
            mobileParty.LeaderHero.IsFemale ||
            mobileParty.PrisonRoster.TotalHeroes <= 0 ||
            MCMSettings.Instance.PrisonPregnancyProbability <= 0) return;

        if (MCMSettings.Instance.PrisonOnlyPlayerParty && mobileParty.LeaderHero != Hero.MainHero) return;

        for (int i = 0; i < mobileParty.PrisonRoster.Count; i++)
        {
            CharacterObject characterObject = mobileParty.PrisonRoster.GetCharacterAtIndex(i);
            if (characterObject.IsHero && characterObject.IsFemale && !characterObject.HeroObject.IsPregnant &&
                characterObject.HeroObject.Clan != null &&
                characterObject.HeroObject.Age >= MCMSettings.Instance.MinPregnancyAge
                && characterObject.HeroObject.Age <= MCMSettings.Instance.MaxPregnancyAge
               )
            {
                MakePrisonerPregnancy(characterObject.HeroObject, mobileParty.LeaderHero);
            }
        }
    }

    private void SettlementPregnancy(Settlement settlement)
    {
        if (settlement.IsTown)
        {
            Town town = settlement.Town;
            if (town.Governor != null)
            {
                if (MCMSettings.Instance.PrisonOnlyPlayerParty && town.Governor.Clan != Clan.PlayerClan) return;
                if (town.Governor.IsFemale) return;
                var prisons = town.GetPrisonerHeroes().FindAll(h => h.IsFemale);
                if (prisons.Count <= 0) return;
                foreach (var prison in prisons)
                {
                    MakePrisonerPregnancy(town.Governor, prison.HeroObject);
                }
            }
        }
    }

    private void MakePrisonerPregnancy(Hero femaleHero, Hero maleHero)
    {
        if (femaleHero.IsPregnant || maleHero.IsFemale) return;
        if (MBRandom.RandomFloat > MCMSettings.Instance.PrisonPregnancyProbability / 100)
            return;

        RecordPregnancyData(femaleHero, maleHero);
        ShowMessage(maleHero, femaleHero);
    }


    private void ShowMessage(Hero man, Hero woman)
    {
        TextObject message = MessageText.PrisonPregnancyMessage;
        message.SetTextVariable("man", man.Name);
        message.SetTextVariable("clan", woman.Clan.Name);
        message.SetTextVariable("woman", woman.Name);
        Log.CommonMessage(message, TextColor.Yellow);
    }


    private void ChildConceived(Hero mother)
    {
        if (mother.Spouse == null) return;
        HeroPregnancies.Add(
            new CustomerPregnancy(mother, mother.Spouse,
                CampaignTime.DaysFromNow(Campaign.Current.Models.PregnancyModel.PregnancyDurationInDays)));
    }

    public void RecordPregnancyData(Hero mother, Hero father)
    {
        try
        {
            mother.IsPregnant = true;
            HeroPregnancies.Add(
                new CustomerPregnancy(mother, father,
                    CampaignTime.DaysFromNow(Campaign.Current.Models.PregnancyModel.PregnancyDurationInDays)));
            LogEntry.AddLogEntry(new PregnancyLogEntry(mother));
        }
        catch (Exception)
        {
            
        }
    }

    private new void OnHeroKilled(Hero victim, Hero killer, KillCharacterAction.KillCharacterActionDetail detail,
        bool showNotification)
    {
        if (victim.IsFemale && HeroPregnancies.Any(p => p.Mother == victim))
        {
            HeroPregnancies.RemoveAll(p => p.Mother == victim);
        }
    }

    public override void SyncData(IDataStore dataStore) => dataStore.SyncData("_heroCustomerPregnancies", ref _heroPregnancies);
}